/**
 * 根据QGIS 3.16.9 LTR版本构建，大量借用了QGIS源码内容
 * by LZS2851
 * 2021年7月20日
 */

//如果使用VS编译器，需要将代码强制转换为UTF-8,并将文件保存为UTF-8编码，才能正确显示中文
#pragma execution_character_set("utf-8")

//qt includes
#include <QBitmap>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QFont>
#include <QFontDatabase>
#include <QPixmap>
#include <QLocale>
#include <QSplashScreen>
#include <QString>
#include <QStringList>
#include <QStyle>
#include <QStyleFactory>
#include <QDesktopWidget>
#include <QImageReader>
#include <QMessageBox>

#include <cstdio>
#include <cstdlib>
#include <cstdarg>

#if !defined(Q_OS_WIN)
#include "sigwatch.h"
#endif

#ifdef WIN32
// Open files in binary mode
#include <fcntl.h> /*  _O_BINARY */
#include <windows.h>
#include <dbghelp.h>
#include <time.h>
#ifdef MSVC
#undef _fmode
int _fmode = _O_BINARY;
#else
// Only do this if we are not building on windows with msvc.
// Recommended method for doing this with msvc is with a call to _set_fmode
// which is the first thing we do in main().
// Similarly, with MinGW set _fmode in main().
#endif  //_MSC_VER
#else
#include <getopt.h>
#endif

//#if defined(PROJ_VERSION_MAJOR) && PROJ_VERSION_MAJOR>=6 && PROJ_VERSION_MAJOR<8
//#define ACCEPT_USE_OF_DEPRECATED_PROJ_API_H
//#endif

#if defined(__GLIBC__) || defined(__FreeBSD__)
#define QGIS_CRASH
#include <unistd.h>
#include <execinfo.h>
#include <csignal>
#include <sys/wait.h>
#include <cerrno>
#endif

#include "qgscustomization.h"
#include "qgssettings.h"
#include "qgsfontutils.h"
#include "qgspluginregistry.h"
#include "qgsmessagelog.h"
#include "qgspythonrunner.h"
#include "qgslocalec.h"
#include "qgisapp.h"
//#include "qrsiagismainwindow.h"

#include "qgsmapcanvas.h"
#include "qgsapplication.h"
#include "qgsconfig.h"
#include "qgsversion.h"
#include "qgsexception.h"
#include "qgsproject.h"
#include "qgsrectangle.h"
#include "qgslogger.h"
#include "qgsdxfexport.h"
#include "qgsmapthemes.h"
#include "qgsvectorlayer.h"
//#include "qgis_app.h"
#include "qgscrashhandler.h"
#include "qgsziputils.h"
#include "qgsversionmigration.h"
#include "qgsfirstrundialog.h"
#include "qgsproxystyle.h"
#include "qgsmessagebar.h"

#include "qgsuserprofilemanager.h"
#include "qgsuserprofile.h"

#ifdef HAVE_OPENCL
#include "qgsopenclutils.h"
#endif

#include "proj_api.h"

/**
 * Print QGIS version
 */
void version( )
{
    const QString msg = QStringLiteral( "QGIS %1 '%2' (%3)\n" ).arg( VERSION ).arg( RELEASE_NAME ).arg( QGSVERSION );
    std::cout << msg.toStdString();
}

/**
 * Print usage text
 */
void usage( const QString &appName )
{
    QStringList msg;

    msg
            << QStringLiteral( "QGIS is a user friendly Open Source Geographic Information System.\n" )
            << QStringLiteral( "Usage: " ) << appName <<  QStringLiteral( " [OPTION] [FILE]\n" )
            << QStringLiteral( "  OPTION:\n" )
            << QStringLiteral( "\t[--version]\tdisplay version information and exit\n" )
            << QStringLiteral( "\t[--snapshot filename]\temit snapshot of loaded datasets to given file\n" )
            << QStringLiteral( "\t[--width width]\twidth of snapshot to emit\n" )
            << QStringLiteral( "\t[--height height]\theight of snapshot to emit\n" )
            << QStringLiteral( "\t[--lang language]\tuse language for interface text (changes existing override)\n" )
            << QStringLiteral( "\t[--project projectfile]\tload the given QGIS project\n" )
            << QStringLiteral( "\t[--extent xmin,ymin,xmax,ymax]\tset initial map extent\n" )
            << QStringLiteral( "\t[--nologo]\thide splash screen\n" )
            << QStringLiteral( "\t[--noversioncheck]\tdon't check for new version of QGIS at startup\n" )
            << QStringLiteral( "\t[--noplugins]\tdon't restore plugins on startup\n" )
            << QStringLiteral( "\t[--nocustomization]\tdon't apply GUI customization\n" )
            << QStringLiteral( "\t[--customizationfile path]\tuse the given ini file as GUI customization\n" )
            << QStringLiteral( "\t[--globalsettingsfile path]\tuse the given ini file as Global Settings (defaults)\n" )
            << QStringLiteral( "\t[--authdbdirectory path] use the given directory for authentication database\n" )
            << QStringLiteral( "\t[--code path]\trun the given python file on load\n" )
            << QStringLiteral( "\t[--defaultui]\tstart by resetting user ui settings to default\n" )
            << QStringLiteral( "\t[--hide-browser]\thide the browser widget\n" )
            << QStringLiteral( "\t[--dxf-export filename.dxf]\temit dxf output of loaded datasets to given file\n" )
            << QStringLiteral( "\t[--dxf-extent xmin,ymin,xmax,ymax]\tset extent to export to dxf\n" )
            << QStringLiteral( "\t[--dxf-symbology-mode none|symbollayer|feature]\tsymbology mode for dxf output\n" )
            << QStringLiteral( "\t[--dxf-scale-denom scale]\tscale for dxf output\n" )
            << QStringLiteral( "\t[--dxf-encoding encoding]\tencoding to use for dxf output\n" )
            << QStringLiteral( "\t[--dxf-map-theme maptheme]\tmap theme to use for dxf output\n" )
            << QStringLiteral( "\t[--take-screenshots output_path]\ttake screen shots for the user documentation\n" )
            << QStringLiteral( "\t[--screenshots-categories categories]\tspecify the categories of screenshot to be used (see QgsAppScreenShots::Categories).\n" )
            << QStringLiteral( "\t[--profile name]\tload a named profile from the users profiles folder.\n" )
            << QStringLiteral( "\t[--profiles-path path]\tpath to store user profile folders. Will create profiles inside a {path}\\profiles folder \n" )
            << QStringLiteral( "\t[--version-migration]\tforce the settings migration from older version if found\n" )
           #ifdef HAVE_OPENCL
            << QStringLiteral( "\t[--openclprogramfolder]\t\tpath to the folder containing the sources for OpenCL programs.\n" )
           #endif
            << QStringLiteral( "\t[--help]\t\tthis text\n" )
            << QStringLiteral( "\t[--]\t\ttreat all following arguments as FILEs\n\n" )
            << QStringLiteral( "  FILE:\n" )
            << QStringLiteral( "    Files specified on the command line can include rasters,\n" )
            << QStringLiteral( "    vectors, and QGIS project files (.qgs and .qgz): \n" )
            << QStringLiteral( "     1. Rasters - supported formats include GeoTiff, DEM \n" )
            << QStringLiteral( "        and others supported by GDAL\n" )
            << QStringLiteral( "     2. Vectors - supported formats include ESRI Shapefiles\n" )
            << QStringLiteral( "        and others supported by OGR and PostgreSQL layers using\n" )
            << QStringLiteral( "        the PostGIS extension\n" )  ; // OK

} // usage()


/////////////////////////////////////////////////////////////////
// Command line options 'behavior' flag setup
////////////////////////////////////////////////////////////////

// These two are global so that they can be set by the OpenDocuments
// AppleEvent handler as well as by the main routine argv processing

// This behavior will cause QGIS to autoload a project
static QString sProjectFileName;

// This is the 'leftover' arguments collection
static QStringList sFileList;


/* Test to determine if this program was started on Mac OS X by double-clicking
 * the application bundle rather then from a command line. If clicked, argv[1]
 * contains a process serial number in the form -psn_0_1234567. Don't process
 * the command line arguments in this case because argv[1] confuses the processing.
 */
bool bundleclicked( int argc, char *argv[] )
{
    return ( argc > 1 && memcmp( argv[1], "-psn_", 5 ) == 0 );
}

void myPrint( const char *fmt, ... )
{
    va_list ap;
    va_start( ap, fmt );
#if defined(Q_OS_WIN)
    char buffer[1024];
    vsnprintf( buffer, sizeof buffer, fmt, ap );
#else
    vfprintf( stderr, fmt, ap );
#endif
    va_end( ap );
}

static void dumpBacktrace( unsigned int depth )
{
    if ( depth == 0 )
        depth = 20;

#ifdef QGIS_CRASH
    // Below there is a bunch of operations that are not safe in multi-threaded
    // environment (dup()+close() combo, wait(), juggling with file descriptors).
    // Maybe some problems could be resolved with dup2() and waitpid(), but it seems
    // that if the operations on descriptors are not serialized, things will get nasty.
    // That's why there's this lovely mutex here...
    static QMutex sMutex;
    QMutexLocker locker( &sMutex );

    int stderr_fd = -1;
    if ( access( "/usr/bin/c++filt", X_OK ) < 0 )
    {
        myPrint( "Stacktrace (c++filt NOT FOUND):\n" );
    }
    else
    {
        int fd[2];

        if ( pipe( fd ) == 0 && fork() == 0 )
        {
            close( STDIN_FILENO ); // close stdin

            // stdin from pipe
            if ( dup( fd[0] ) != STDIN_FILENO )
            {
                QgsDebugMsg( QStringLiteral( "dup to stdin failed" ) );
            }

            close( fd[1] );        // close writing end
            execl( "/usr/bin/c++filt", "c++filt", static_cast< char * >( nullptr ) );
            perror( "could not start c++filt" );
            exit( 1 );
        }

        myPrint( "Stacktrace (piped through c++filt):\n" );
        stderr_fd = dup( STDERR_FILENO );
        close( fd[0] );          // close reading end
        close( STDERR_FILENO );  // close stderr

        // stderr to pipe
        int stderr_new = dup( fd[1] );
        if ( stderr_new != STDERR_FILENO )
        {
            if ( stderr_new >= 0 )
                close( stderr_new );
            QgsDebugMsg( QStringLiteral( "dup to stderr failed" ) );
        }

        close( fd[1] );  // close duped pipe
    }

    void **buffer = new void *[ depth ];
    int nptrs = backtrace( buffer, depth );
    backtrace_symbols_fd( buffer, nptrs, STDERR_FILENO );
    delete [] buffer;
    if ( stderr_fd >= 0 )
    {
        int status;
        close( STDERR_FILENO );
        int dup_stderr = dup( stderr_fd );
        if ( dup_stderr != STDERR_FILENO )
        {
            close( dup_stderr );
            QgsDebugMsg( QStringLiteral( "dup to stderr failed" ) );
        }
        close( stderr_fd );
        wait( &status );
    }
#elif defined(Q_OS_WIN)
    // TODO Replace with incoming QgsStackTrace
#else
    Q_UNUSED( depth )
#endif
}

#ifdef QGIS_CRASH
void qgisCrash( int signal )
{
    fprintf( stderr, "QGIS died on signal %d", signal );

    if ( access( "/usr/bin/gdb", X_OK ) == 0 )
    {
        // take full stacktrace using gdb
        // http://stackoverflow.com/questions/3151779/how-its-better-to-invoke-gdb-from-program-to-print-its-stacktrace
        // unfortunately, this is not so simple. the proper method is way more OS-specific
        // than this code would suggest, see http://stackoverflow.com/a/1024937

        char exename[512];
#if defined(__FreeBSD__)
        int len = readlink( "/proc/curproc/file", exename, sizeof( exename ) - 1 );
#else
        int len = readlink( "/proc/self/exe", exename, sizeof( exename ) - 1 );
#endif
        if ( len < 0 )
        {
            myPrint( "Could not read link (%d: %s)\n", errno, strerror( errno ) );
        }
        else
        {
            exename[ len ] = 0;

            char pidstr[32];
            snprintf( pidstr, sizeof pidstr, "--pid=%d", getpid() );

            int gdbpid = fork();
            if ( gdbpid == 0 )
            {
                // attach, backtrace and continue
                execl( "/usr/bin/gdb", "gdb", "-q", "-batch", "-n", pidstr, "-ex", "thread", "-ex", "bt full", exename, NULL );
                perror( "cannot exec gdb" );
                exit( 1 );
            }
            else if ( gdbpid >= 0 )
            {
                int status;
                waitpid( gdbpid, &status, 0 );
                myPrint( "gdb returned %d\n", status );
            }
            else
            {
                myPrint( "Cannot fork (%d: %s)\n", errno, strerror( errno ) );
                dumpBacktrace( 256 );
            }
        }
    }

    abort();
}
#endif

/*
 * Hook into the qWarning/qFatal mechanism so that we can channel messages
 * from libpng to the user.
 *
 * Some JPL WMS images tend to overload the libpng 1.2.2 implementation
 * somehow (especially when zoomed in)
 * and it would be useful for the user to know why their picture turned up blank
 *
 * Based on qInstallMsgHandler example code in the Qt documentation.
 *
 */
void myMessageOutput( QtMsgType type, const QMessageLogContext &, const QString &msg )
{
    switch ( type )
    {
    case QtDebugMsg:
        myPrint( "%s\n", msg.toLocal8Bit().constData() );
        if ( msg.startsWith( QLatin1String( "Backtrace" ) ) )
        {
            const QString trace = msg.mid( 9 );
            dumpBacktrace( atoi( trace.toLocal8Bit().constData() ) );
        }
        break;
    case QtCriticalMsg:
        myPrint( "Critical: %s\n", msg.toLocal8Bit().constData() );

#ifdef QGISDEBUG
        dumpBacktrace( 20 );
#endif

        break;
    case QtWarningMsg:
    {
        /* Ignore:
       * - libpng iCPP known incorrect SRGB profile errors (which are thrown by 3rd party components
       *  we have no control over and have low value anyway);
       * - QtSVG warnings with regards to lack of implementation beyond Tiny SVG 1.2
       */
        if ( msg.startsWith( QLatin1String( "libpng warning: iCCP: known incorrect sRGB profile" ), Qt::CaseInsensitive ) ||
             msg.contains( QLatin1String( "Could not add child element to parent element because the types are incorrect" ), Qt::CaseInsensitive ) )
            break;

        myPrint( "Warning: %s\n", msg.toLocal8Bit().constData() );

#ifdef QGISDEBUG
        // Print all warnings except setNamedColor.
        // Only seems to happen on windows
        if ( !msg.startsWith( QLatin1String( "QColor::setNamedColor: Unknown color name 'param" ), Qt::CaseInsensitive )
             && !msg.startsWith( QLatin1String( "Trying to create a QVariant instance of QMetaType::Void type, an invalid QVariant will be constructed instead" ), Qt::CaseInsensitive )
             && !msg.startsWith( QLatin1String( "Logged warning" ), Qt::CaseInsensitive ) )
        {
            // TODO: Verify this code in action.
            dumpBacktrace( 20 );

            // also be super obnoxious -- we DON'T want to allow these errors to be ignored!!
            if ( QRSIAGISMainWindow::instance() && QRSIAGISMainWindow::instance()->messageBar() && QRSIAGISMainWindow::instance()->thread() == QThread::currentThread() )
            {
                QRSIAGISMainWindow::instance()->messageBar()->pushCritical( QStringLiteral( "Qt" ), msg );
            }
            else
            {
                QgsMessageLog::logMessage( msg, QStringLiteral( "Qt" ) );
            }
        }
#endif

        // TODO: Verify this code in action.
        if ( msg.startsWith( QLatin1String( "libpng error:" ), Qt::CaseInsensitive ) )
        {
            // Let the user know
            QgsMessageLog::logMessage( msg, QStringLiteral( "libpng" ) );
        }

        break;
    }

    case QtFatalMsg:
    {
        myPrint( "Fatal: %s\n", msg.toLocal8Bit().constData() );
#ifdef QGIS_CRASH
        qgisCrash( -1 );
#else
        dumpBacktrace( 256 );
        abort();                    // deliberately dump core
#endif
        break; // silence warnings
    }

    case QtInfoMsg:
        myPrint( "Info: %s\n", msg.toLocal8Bit().constData() );
        break;
    }
}

//启动程序的主函数
int main( int argc, char *argv[] )
{
    //log messages written before creating QgsApplication
    QStringList preApplicationLogMessages;

    QgsDebugMsg(QStringLiteral("TestTranslation001..."));
    QgsDebugMsg(tr("TestTranslation001..."));

    QgsDebugMsg( QStringLiteral( "Starting qgis main" ) );
#ifdef WIN32  // Windows
#ifdef _MSC_VER
    _set_fmode( _O_BINARY );
#else //MinGW
    _fmode = _O_BINARY;
#endif  // _MSC_VER
#endif  // WIN32


#ifdef QGIS_CRASH
    signal( SIGQUIT, qgisCrash );
    signal( SIGILL, qgisCrash );
    signal( SIGFPE, qgisCrash );
    signal( SIGSEGV, qgisCrash );
    signal( SIGBUS, qgisCrash );
    signal( SIGSYS, qgisCrash );
    signal( SIGTRAP, qgisCrash );
    signal( SIGXCPU, qgisCrash );
    signal( SIGXFSZ, qgisCrash );
#endif

#ifdef _MSC_VER
    SetUnhandledExceptionFilter( QgsCrashHandler::handle );
#endif

    // initialize random number seed
    qsrand( time( nullptr ) );

    /////////////////////////////////////////////////////////////////
    // Command line options 'behavior' flag setup
    ////////////////////////////////////////////////////////////////

    //
    // Parse the command line arguments, looking to see if the user has asked for any
    // special behaviors. Any remaining non command arguments will be kept aside to
    // be passed as a list of layers and / or a project that should be loaded.
    //

    // This behavior is used to load the app, snapshot the map,
    // save the image to disk and then exit
    QString mySnapshotFileName;
    QString configLocalStorageLocation;
    QString profileName;
    int mySnapshotWidth = 800;
    int mySnapshotHeight = 600;

    bool myHideSplash = false;
    bool settingsMigrationForce = false;
    bool mySkipVersionCheck = false;
    bool hideBrowser = false;

    bool myRestoreDefaultWindowState = false;
    bool myRestorePlugins = true;
    bool myCustomization = true;

    QString dxfOutputFile;
    QgsDxfExport::SymbologyExport dxfSymbologyMode = QgsDxfExport::SymbolLayerSymbology;
    double dxfScale = 50000.0;
    QString dxfEncoding = QStringLiteral( "CP1252" );
    QString dxfMapTheme;
    QgsRectangle dxfExtent;

    bool takeScreenShots = false;
    QString screenShotsPath;
    int screenShotsCategories = 0;

    // This behavior will set initial extent of map canvas, but only if
    // there are no command line arguments. This gives a usable map
    // extent when qgis starts with no layers loaded. When layers are
    // loaded, we let the layers define the initial extent.
    QString myInitialExtent;
    if ( argc == 1 )
        myInitialExtent = QStringLiteral( "-1,-1,1,1" );

    // This behavior will allow you to force the use of a translation file
    // which is useful for testing
    QString translationCode;

    // The user can specify a path which will override the default path of custom
    // user settings (~/.qgis) and it will be used for QgsSettings INI file
    QString authdbdirectory;

    QString pythonfile;

    QString customizationfile;
    QString globalsettingsfile;

#ifdef HAVE_OPENCL
    QString openClProgramFolder;
#endif

    QStringList args;
    {
        QCoreApplication coreApp( argc, argv );
        ( void ) QgsApplication::resolvePkgPath(); // trigger storing of application path in QgsApplication

        if ( !bundleclicked( argc, argv ) )
        {
            // Build a local QCoreApplication from arguments. This way, arguments are correctly parsed from their native locale
            // It will use QString::fromLocal8Bit( argv ) under Unix and GetCommandLine() under Windows.
            args = QCoreApplication::arguments();

            for ( int i = 1; i < args.size(); ++i )
            {
                const QString &arg = args[i];

                if ( arg == QLatin1String( "--help" ) || arg == QLatin1String( "-?" ) )
                {
                    usage( args[0] );
                    return EXIT_SUCCESS;
                }
                else if ( arg == QLatin1String( "--version" ) || arg == QLatin1String( "-v" ) )
                {
                    version();
                    return EXIT_SUCCESS;
                }
                else if ( arg == QLatin1String( "--nologo" ) || arg == QLatin1String( "-n" ) )
                {
                    myHideSplash = true;
                }
                else if ( arg == QLatin1String( "--version-migration" ) )
                {
                    settingsMigrationForce = true;
                }
                else if ( arg == QLatin1String( "--noversioncheck" ) || arg == QLatin1String( "-V" ) )
                {
                    mySkipVersionCheck = true;
                }
                else if ( arg == QLatin1String( "--noplugins" ) || arg == QLatin1String( "-P" ) )
                {
                    myRestorePlugins = false;
                }
                else if ( arg == QLatin1String( "--nocustomization" ) || arg == QLatin1String( "-C" ) )
                {
                    myCustomization = false;
                }
                else if ( i + 1 < argc && ( arg == QLatin1String( "--profile" ) ) )
                {
                    profileName = args[++i];
                }
                else if ( i + 1 < argc && ( arg == QLatin1String( "--profiles-path" ) || arg == QLatin1String( "-s" ) ) )
                {
                    configLocalStorageLocation = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() );
                }
                else if ( i + 1 < argc && ( arg == QLatin1String( "--snapshot" ) || arg == QLatin1String( "-s" ) ) )
                {
                    mySnapshotFileName = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() );
                }
                else if ( i + 1 < argc && ( arg == QLatin1String( "--width" ) || arg == QLatin1String( "-w" ) ) )
                {
                    mySnapshotWidth = QString( args[++i] ).toInt();
                }
                else if ( arg == QLatin1String( "--hide-browser" ) )
                {
                    hideBrowser = true;
                }
                else if ( i + 1 < argc && ( arg == QLatin1String( "--height" ) || arg == QLatin1String( "-h" ) ) )
                {
                    mySnapshotHeight = QString( args[++i] ).toInt();
                }
                else if ( i + 1 < argc && ( arg == QLatin1String( "--lang" ) || arg == QLatin1String( "-l" ) ) )
                {
                    translationCode = args[++i];
                }
                else if ( i + 1 < argc && ( arg == QLatin1String( "--project" ) || arg == QLatin1String( "-p" ) ) )
                {
                    sProjectFileName = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() );
                }
                else if ( i + 1 < argc && ( arg == QLatin1String( "--extent" ) || arg == QLatin1String( "-e" ) ) )
                {
                    myInitialExtent = args[++i];
                }
                else if ( i + 1 < argc && ( arg == QLatin1String( "--authdbdirectory" ) || arg == QLatin1String( "-a" ) ) )
                {
                    authdbdirectory = QDir::toNativeSeparators( QDir( args[++i] ).absolutePath() );
                }
                else if ( i + 1 < argc && ( arg == QLatin1String( "--code" ) || arg == QLatin1String( "-f" ) ) )
                {
                    pythonfile = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() );
                }
                else if ( i + 1 < argc && ( arg == QLatin1String( "--customizationfile" ) || arg == QLatin1String( "-z" ) ) )
                {
                    customizationfile = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() );
                }
                else if ( i + 1 < argc && ( arg == QLatin1String( "--globalsettingsfile" ) || arg == QLatin1String( "-g" ) ) )
                {
                    globalsettingsfile = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() );
                }
                else if ( arg == QLatin1String( "--defaultui" ) || arg == QLatin1String( "-d" ) )
                {
                    myRestoreDefaultWindowState = true;
                }
                else if ( arg == QLatin1String( "--dxf-export" ) )
                {
                    dxfOutputFile = args[++i];
                }
                else if ( arg == QLatin1String( "--dxf-extent" ) )
                {
                    QgsLocaleNumC l;
                    QString ext( args[++i] );
                    QStringList coords( ext.split( ',' ) );

                    if ( coords.size() != 4 )
                    {
                        std::cerr << "invalid dxf extent " << ext.toStdString() << std::endl;
                        return 2;
                    }

                    for ( int i = 0; i < 4; i++ )
                    {
                        bool ok;
                        double d;

                        d = coords[i].toDouble( &ok );
                        if ( !ok )
                        {
                            std::cerr << "invalid dxf coordinate " << coords[i].toStdString() << " in extent " << ext.toStdString() << std::endl;
                            return 2;
                        }

                        switch ( i )
                        {
                        case 0:
                            dxfExtent.setXMinimum( d );
                            break;
                        case 1:
                            dxfExtent.setYMinimum( d );
                            break;
                        case 2:
                            dxfExtent.setXMaximum( d );
                            break;
                        case 3:
                            dxfExtent.setYMaximum( d );
                            break;
                        }
                    }
                }
                else if ( arg == QLatin1String( "--dxf-symbology-mode" ) )
                {
                    QString mode( args[++i] );
                    if ( mode == QLatin1String( "none" ) )
                    {
                        dxfSymbologyMode = QgsDxfExport::NoSymbology;
                    }
                    else if ( mode == QLatin1String( "symbollayer" ) )
                    {
                        dxfSymbologyMode = QgsDxfExport::SymbolLayerSymbology;
                    }
                    else if ( mode == QLatin1String( "feature" ) )
                    {
                        dxfSymbologyMode = QgsDxfExport::FeatureSymbology;
                    }
                    else
                    {
                        std::cerr << "invalid dxf symbology mode " << mode.toStdString() << std::endl;
                        return 2;
                    }
                }
                else if ( arg == QLatin1String( "--dxf-scale-denom" ) )
                {
                    bool ok;
                    QString scale( args[++i] );
                    dxfScale = scale.toDouble( &ok );
                    if ( !ok )
                    {
                        std::cerr << "invalid dxf scale " << scale.toStdString() << std::endl;
                        return 2;
                    }
                }
                else if ( arg == QLatin1String( "--dxf-encoding" ) )
                {
                    dxfEncoding = args[++i];
                }
                else if ( arg == QLatin1String( "--dxf-map-theme" ) )
                {
                    dxfMapTheme = args[++i];
                }
                else if ( arg == QLatin1String( "--take-screenshots" ) )
                {
                    takeScreenShots = true;
                    screenShotsPath = args[++i];
                }
                else if ( arg == QLatin1String( "--screenshots-categories" ) )
                {
                    screenShotsCategories = args[++i].toInt();
                }
#ifdef HAVE_OPENCL
                else if ( arg == QLatin1String( "--openclprogramfolder" ) )
                {
                    openClProgramFolder = args[++i];
                }
#endif
                else if ( arg == QLatin1String( "--" ) )
                {
                    for ( i++; i < args.size(); ++i )
                        sFileList.append( QDir::toNativeSeparators( QFileInfo( args[i] ).absoluteFilePath() ) );
                }
                else
                {
                    sFileList.append( QDir::toNativeSeparators( QFileInfo( args[i] ).absoluteFilePath() ) );
                }
            }
        }
    }

    /////////////////////////////////////////////////////////////////////
    // If no --project was specified, parse the args to look for a     //
    // .qgs file and set myProjectFileName to it. This allows loading  //
    // of a project file by clicking on it in various desktop managers //
    // where an appropriate mime-type has been set up.                 //
    /////////////////////////////////////////////////////////////////////
    if ( sProjectFileName.isEmpty() )
    {
        // check for a .qgs/z
        for ( int i = 0; i < args.size(); i++ )
        {
            QString arg = QDir::toNativeSeparators( QFileInfo( args[i] ).absoluteFilePath() );
            if ( arg.endsWith( QLatin1String( ".qgs" ), Qt::CaseInsensitive ) ||
                 arg.endsWith( QLatin1String( ".qgz" ), Qt::CaseInsensitive ) )
            {
                sProjectFileName = arg;
                break;
            }
        }
    }

    /////////////////////////////////////////////////////////////////////
    // Now we have the handlers for the different behaviors...
    /////////////////////////////////////////////////////////////////////

    /////////////////////////////////////////////////////////////////////
    // Initialize the application and the translation stuff
    /////////////////////////////////////////////////////////////////////
    bool myUseGuiFlag = true;
    if ( !myUseGuiFlag )
    {
        std::cerr << QObject::tr(
                         "QGIS starting in non-interactive mode not supported.\n"
                         "You are seeing this message most likely because you "
                         "have no DISPLAY environment variable set.\n"
                         ).toUtf8().constData();
        exit( 1 ); //exit for now until a version of qgis is capable of running non interactive
    }

    // GUI customization is enabled according to settings (loaded when instance is created)
    // we force disabled here if --nocustomization argument is used
    if ( !myCustomization )
    {
        QgsCustomization::instance()->setEnabled( false );
    }

    QCoreApplication::setOrganizationName( QgsApplication::QGIS_ORGANIZATION_NAME );
    QCoreApplication::setOrganizationDomain( QgsApplication::QGIS_ORGANIZATION_DOMAIN );
    QCoreApplication::setApplicationName( QgsApplication::QGIS_APPLICATION_NAME );
    QCoreApplication::setAttribute( Qt::AA_DontShowIconsInMenus, false );
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
    QCoreApplication::setAttribute( Qt::AA_DisableWindowContextHelpButton, true );
#endif

    // Set up the QgsSettings Global Settings:
    // - use the path specified with --globalsettingsfile path,
    // - use the environment if not found
    // - use the AppDataLocation ($HOME/.local/share/QGIS/QGIS3 on Linux, roaming path on Windows)
    // - use a default location as a fallback
    if ( globalsettingsfile.isEmpty() )
    {
        globalsettingsfile = getenv( "QGIS_GLOBAL_SETTINGS_FILE" );
    }

    if ( globalsettingsfile.isEmpty() )
    {
        QStringList startupPaths = QStandardPaths::locateAll( QStandardPaths::AppDataLocation, QStringLiteral( "qgis_global_settings.ini" ) );
        if ( !startupPaths.isEmpty() )
        {
            globalsettingsfile = startupPaths.at( 0 );
        }
    }

    if ( globalsettingsfile.isEmpty() )
    {
        QString default_globalsettingsfile = QgsApplication::resolvePkgPath() + "/resources/qgis_global_settings.ini";

        if ( QFile::exists( default_globalsettingsfile ) )
        {
            globalsettingsfile = default_globalsettingsfile;
        }
    }

    if ( !globalsettingsfile.isEmpty() )
    {
        if ( !QgsSettings::setGlobalSettingsPath( globalsettingsfile ) )
        {
            preApplicationLogMessages << QObject::tr( "Invalid globalsettingsfile path: %1" ).arg( globalsettingsfile ), QStringLiteral( "QGIS" );
        }
        else
        {
            preApplicationLogMessages << QObject::tr( "Successfully loaded globalsettingsfile path: %1" ).arg( globalsettingsfile ), QStringLiteral( "QGIS" );
        }
    }

    if ( configLocalStorageLocation.isEmpty() )
    {
        QSettings globalSettings( globalsettingsfile, QSettings::IniFormat );
        if ( getenv( "QGIS_CUSTOM_CONFIG_PATH" ) )
        {
            configLocalStorageLocation = getenv( "QGIS_CUSTOM_CONFIG_PATH" );
        }
        else if ( globalSettings.contains( QStringLiteral( "core/profilesPath" ) ) )
        {
            configLocalStorageLocation = globalSettings.value( QStringLiteral( "core/profilesPath" ), "" ).toString();
            QgsDebugMsg( QStringLiteral( "Loading profiles path from global config at %1" ).arg( configLocalStorageLocation ) );
        }

        // If it is still empty at this point we get it from the standard location.
        if ( configLocalStorageLocation.isEmpty() )
        {
            QString executePath = QgsApplication::resolvePkgPath();
            configLocalStorageLocation = executePath.left(executePath.length() - 1) + "configLocalStorage";
        }
    }

    QString rootProfileFolder = QgsUserProfileManager::resolveProfilesFolder( configLocalStorageLocation );
    QgsUserProfileManager manager( rootProfileFolder );
    QgsUserProfile *profile = manager.getProfile( profileName, true );
    QString profileFolder = profile->folder();
    profileName = profile->name();
    delete profile;

    {
        QgsSettings settings;

        /* Translation file for QGIS.
    */
        QString myUserTranslation = settings.value( QStringLiteral( "locale/userLocale" ), "" ).toString();
        QString myGlobalLocale = settings.value( QStringLiteral( "locale/globalLocale" ), "" ).toString();
        bool myShowGroupSeparatorFlag = false; // Default to false
        bool myLocaleOverrideFlag = settings.value( QStringLiteral( "locale/overrideFlag" ), false ).toBool();

        // Override Show Group Separator if the global override flag is set
        if ( myLocaleOverrideFlag )
        {
            // Default to false again
            myShowGroupSeparatorFlag = settings.value( QStringLiteral( "locale/showGroupSeparator" ), false ).toBool();
        }

        //
        // Priority of translation is:
        //  - command line
        //  - user specified in options dialog (with group checked on)
        //  - system locale
        //
        //  When specifying from the command line it will change the user
        //  specified user locale
        //
        if ( !translationCode.isNull() && !translationCode.isEmpty() )
        {
            settings.setValue( QStringLiteral( "locale/userLocale" ), translationCode );
        }
        else
        {
            if ( !myLocaleOverrideFlag || myUserTranslation.isEmpty() )
            {
                translationCode = QLocale().name();
                //setting the locale/userLocale when the --lang= option is not set will allow third party
                //plugins to always use the same locale as the QGIS, otherwise they can be out of sync
                settings.setValue( QStringLiteral( "locale/userLocale" ), translationCode );
            }
            else
            {
                translationCode = myUserTranslation;
            }
        }

        // Global locale settings
        if ( myLocaleOverrideFlag && ! myGlobalLocale.isEmpty( ) )
        {
            QLocale currentLocale( myGlobalLocale );
            QLocale::setDefault( currentLocale );
        }

        // Number settings
        QLocale currentLocale;
        if ( myShowGroupSeparatorFlag )
        {
            currentLocale.setNumberOptions( currentLocale.numberOptions() &= ~QLocale::NumberOption::OmitGroupSeparator );
        }
        else
        {
            currentLocale.setNumberOptions( currentLocale.numberOptions() |= QLocale::NumberOption::OmitGroupSeparator );
        }
        QLocale::setDefault( currentLocale );

        QgsApplication::setTranslation( translationCode );
    }

    QgsApplication myApp( argc, argv, myUseGuiFlag );

    //write the log messages written before creating QgsApplication
    for ( const QString &preApplicationLogMessage : qgis::as_const( preApplicationLogMessages ) )
        QgsMessageLog::logMessage( preApplicationLogMessage );

    // Settings migration is only supported on the default profile for now.
    if ( profileName == QLatin1String( "default" ) )
    {
        // Note: this flag is ka version number so that we can reset it once we change the version.
        // Note2: Is this a good idea can we do it better.

        QgsSettings migSettings;
        int firstRunVersion = migSettings.value( QStringLiteral( "migration/firstRunVersionFlag" ), 0 ).toInt();
        bool showWelcome = ( firstRunVersion == 0  || Qgis::versionInt() > firstRunVersion );

        std::unique_ptr< QgsVersionMigration > migration( QgsVersionMigration::canMigrate( 20000, Qgis::versionInt() ) );
        if ( migration && ( settingsMigrationForce || migration->requiresMigration() ) )
        {
            bool runMigration = true;
            if ( !settingsMigrationForce && showWelcome )
            {
                QgsFirstRunDialog dlg;
                if ( ! QFile::exists( QSettings( "QGIS", "QGIS2" ).fileName() ) )
                {
                    dlg.hideMigration();
                }
                dlg.exec();
                runMigration = dlg.migrateSettings();
                migSettings.setValue( QStringLiteral( "migration/firstRunVersionFlag" ), Qgis::versionInt() );
            }

            if ( runMigration )
            {
                QgsDebugMsg( QStringLiteral( "RUNNING MIGRATION" ) );
                migration->runMigration();
            }
        }
    }

    // We can't use QgsSettings until this point because the format and
    // folder isn't set until profile is fetch.
    // Should be cleaned up in future to make this cleaner.
    QgsSettings settings;

    QgsDebugMsgLevel( QStringLiteral( "User profile details:" ), 2 );
    QgsDebugMsgLevel( QStringLiteral( "\t - %1" ).arg( profileName ), 2 );
    QgsDebugMsgLevel( QStringLiteral( "\t - %1" ).arg( profileFolder ), 2 );
    QgsDebugMsgLevel( QStringLiteral( "\t - %1" ).arg( rootProfileFolder ), 2 );

    myApp.init( profileFolder );

    // Redefine QgsApplication::libraryPaths as necessary.
    // IMPORTANT: Do *after* QgsApplication myApp(...), but *before* Qt uses any plugins,
    //            e.g. loading splash screen, setting window icon, etc.
    //            Always honor QT_PLUGIN_PATH env var or qt.conf, which will
    //            be part of libraryPaths just after QgsApplication creation.
#ifdef Q_OS_WIN
    // For non static builds on win (static builds are not supported)
    // we need to be sure we can find the qt image plugins.
    QCoreApplication::addLibraryPath( QApplication::applicationDirPath()
                                      + QDir::separator() + "qtplugins" );
#endif

    //    myApp.setWindowIcon( QIcon( QgsApplication::appIconPath() ) );
    myApp.setWindowIcon( QIcon("qrc:/images/myImages/appIcon.png") );

    // TODO: use QgsSettings
    QSettings *customizationsettings = nullptr;

    if ( !customizationfile.isEmpty() )
    {
        // Using the customizationfile option always overrides the option and config path options.
        QgsCustomization::instance()->setEnabled( true );
    }
    else
    {
        // Use the default file location
        customizationfile = profileFolder + QDir::separator() + QStringLiteral( "QGIS" ) + QDir::separator() + QStringLiteral( "QGISCUSTOMIZATION3.ini" ) ;
    }

    customizationsettings = new QSettings( customizationfile, QSettings::IniFormat );

    // Load and set possible default customization, must be done after QgsApplication init and QgsSettings ( QCoreApplication ) init
    QgsCustomization::instance()->setSettings( customizationsettings );
    QgsCustomization::instance()->loadDefault();

    // custom environment variables
    QMap<QString, QString> systemEnvVars = QgsApplication::systemEnvVars();
    bool useCustomVars = settings.value( QStringLiteral( "qgis/customEnvVarsUse" ), QVariant( false ) ).toBool();
    if ( useCustomVars )
    {
        QStringList customVarsList = settings.value( QStringLiteral( "qgis/customEnvVars" ), "" ).toStringList();
        if ( !customVarsList.isEmpty() )
        {
            const auto constCustomVarsList = customVarsList;
            for ( const QString &varStr : constCustomVarsList )
            {
                int pos = varStr.indexOf( QLatin1Char( '|' ) );
                if ( pos == -1 )
                    continue;
                QString envVarApply = varStr.left( pos );
                QString varStrNameValue = varStr.mid( pos + 1 );
                pos = varStrNameValue.indexOf( QLatin1Char( '=' ) );
                if ( pos == -1 )
                    continue;
                QString envVarName = varStrNameValue.left( pos );
                QString envVarValue = varStrNameValue.mid( pos + 1 );

                if ( systemEnvVars.contains( envVarName ) )
                {
                    if ( envVarApply == QLatin1String( "prepend" ) )
                    {
                        envVarValue += systemEnvVars.value( envVarName );
                    }
                    else if ( envVarApply == QLatin1String( "append" ) )
                    {
                        envVarValue = systemEnvVars.value( envVarName ) + envVarValue;
                    }
                }

                if ( systemEnvVars.contains( envVarName ) && envVarApply == QLatin1String( "unset" ) )
                {
#ifdef Q_OS_WIN
                    putenv( envVarName.toUtf8().constData() );
#else
                    unsetenv( envVarName.toUtf8().constData() );
#endif
                }
                else
                {
#ifdef Q_OS_WIN
                    if ( envVarApply != "undefined" || !getenv( envVarName.toUtf8().constData() ) )
                        putenv( QString( "%1=%2" ).arg( envVarName ).arg( envVarValue ).toUtf8().constData() );
#else
                    setenv( envVarName.toUtf8().constData(), envVarValue.toUtf8().constData(), envVarApply == QLatin1String( "undefined" ) ? 0 : 1 );
#endif
                }
            }
        }
    }

#ifdef QGISDEBUG
    QgsFontUtils::loadStandardTestFonts( QStringList() << QStringLiteral( "Roman" ) << QStringLiteral( "Bold" ) );
#endif

    // Set the application style.  If it's not set QT will use the platform style except on Windows
    // as it looks really ugly so we use QPlastiqueStyle.
    QString desiredStyle = settings.value( QStringLiteral( "qgis/style" ) ).toString();
    const QString theme = settings.value( QStringLiteral( "UI/UITheme" ) ).toString();
    if ( theme != QLatin1String( "default" ) )
    {
        if ( QStyleFactory::keys().contains( QStringLiteral( "fusion" ), Qt::CaseInsensitive ) )
        {
            desiredStyle = QStringLiteral( "fusion" );
        }
    }
    const QString activeStyleName = QApplication::style()->metaObject()->className();
    if ( desiredStyle.contains( QLatin1String( "adwaita" ), Qt::CaseInsensitive )
         || ( desiredStyle.isEmpty() && activeStyleName.contains( QLatin1String( "adwaita" ), Qt::CaseInsensitive ) ) )
    {
        //never allow Adwaita themes - the Qt variants of these are VERY broken
        //for apps like QGIS. E.g. oversized controls like spinbox widgets prevent actually showing
        //any content in these widgets, leaving a very bad impression of QGIS

        //note... we only do this if there's a known good style available (fusion), as SOME
        //style choices can cause Qt apps to crash...
        if ( QStyleFactory::keys().contains( QStringLiteral( "fusion" ), Qt::CaseInsensitive ) )
        {
            desiredStyle = QStringLiteral( "fusion" );
        }
    }
    if ( !desiredStyle.isEmpty() )
    {
        QApplication::setStyle( new QgsAppStyle( desiredStyle ) );

        if ( activeStyleName != desiredStyle )
            settings.setValue( QStringLiteral( "qgis/style" ), desiredStyle );
    }
    else
    {
        // even if user has not set a style, we need to override the application style with the QgsAppStyle proxy
        // based on the default style (or we miss custom style tweaks)
        QApplication::setStyle( new QgsAppStyle( activeStyleName ) );
    }

    // set authentication database directory
    if ( !authdbdirectory.isEmpty() )
    {
        QgsApplication::setAuthDatabaseDirPath( authdbdirectory );
    }

    //set up splash screen
    QString mySplashPath( QgsCustomization::instance()->splashPath() );
    QPixmap myPixmap( mySplashPath + QStringLiteral( "mySplash.png" ) );

    int w = 900 * qApp->desktop()->logicalDpiX() / 96;
    int h = 450 * qApp->desktop()->logicalDpiY() / 96;

    QSplashScreen *mypSplash = new QSplashScreen( myPixmap.scaled( w, h, Qt::KeepAspectRatio, Qt::SmoothTransformation ) );
    if ( !takeScreenShots && !myHideSplash && !settings.value( QStringLiteral( "qgis/hideSplash" ) ).toBool() )
    {
        //for win and linux we can just automask and png transparency areas will be used
        mypSplash->setMask( myPixmap.mask() );
        mypSplash->show();
    }

    // optionally restore default window state
    // use restoreDefaultWindowState setting only if NOT using command line (then it is set already)
    if ( myRestoreDefaultWindowState || settings.value( QStringLiteral( "qgis/restoreDefaultWindowState" ), false ).toBool() )
    {
        QgsDebugMsg( QStringLiteral( "Resetting /UI/state settings!" ) );
        settings.remove( QStringLiteral( "/UI/state" ) );
        settings.remove( QStringLiteral( "/qgis/restoreDefaultWindowState" ) );
    }

    if ( hideBrowser )
    {
        if ( settings.value( QStringLiteral( "/Windows/Data Source Manager/tab" ) ).toInt() == 0 )
            settings.setValue( QStringLiteral( "/Windows/Data Source Manager/tab" ), 1 );
        settings.setValue( QStringLiteral( "/UI/hidebrowser" ), true );
    }

    // set max. thread count
    // this should be done in QgsApplication::init() but it doesn't know the settings dir.
    QgsApplication::setMaxThreads( settings.value( QStringLiteral( "qgis/max_threads" ), -1 ).toInt() );

    //设置 proj.db的搜索 路径
    const char* projDBPath[] = {"./share/proj"};
    pj_set_searchpath(1, projDBPath);

    QgisApp *qRSIAGIS = new QgisApp( mypSplash, myRestorePlugins, mySkipVersionCheck, rootProfileFolder, profileName ); //used to find canonical instance

    qRSIAGIS->setObjectName( QStringLiteral( "QRSIAGISMainWindow" ) );
    myApp.connect(
                &myApp, SIGNAL( preNotify( QObject *, QEvent *, bool * ) ),
                //qgis, SLOT( preNotify( QObject *, QEvent *))
                QgsCustomization::instance(), SLOT( preNotify( QObject *, QEvent *, bool * ) )
                );

    /////////////////////////////////////////////////////////////////////
    // Load a project file if one was specified
    /////////////////////////////////////////////////////////////////////
    if ( ! sProjectFileName.isEmpty() )
    {
        // in case the project contains broken layers, interactive
        // "Handle Bad Layers" is displayed that could be blocked by splash screen
        mypSplash->hide();
        qRSIAGIS->openProject( sProjectFileName );
    }

    /////////////////////////////////////////////////////////////////////
    // autoload any file names that were passed in on the command line
    /////////////////////////////////////////////////////////////////////
    for ( const QString &layerName : qgis::as_const( sFileList ) )
    {
        QgsDebugMsg( QStringLiteral( "Trying to load file : %1" ).arg( layerName ) );
        // don't load anything with a .qgs extension - these are project files
        if ( !layerName.endsWith( QLatin1String( ".qgs" ), Qt::CaseInsensitive ) &&
             !layerName.endsWith( QLatin1String( ".qgz" ), Qt::CaseInsensitive ) &&
             !QgsZipUtils::isZipFile( layerName ) )
        {
            qRSIAGIS->openLayer( layerName );
        }
    }


    /////////////////////////////////////////////////////////////////////
    // Set initial extent if requested
    /////////////////////////////////////////////////////////////////////
    if ( ! myInitialExtent.isEmpty() )
    {
        QgsLocaleNumC l;
        double coords[4];
        int pos, posOld = 0;
        bool ok = true;

        // parse values from string
        // extent is defined by string "xmin,ymin,xmax,ymax"
        for ( int i = 0; i < 3; i++ )
        {
            // find comma and get coordinate
            pos = myInitialExtent.indexOf( ',', posOld );
            if ( pos == -1 )
            {
                ok = false;
                break;
            }

            coords[i] = myInitialExtent.midRef( posOld, pos - posOld ).toDouble( &ok );
            if ( !ok )
                break;

            posOld = pos + 1;
        }

        // parse last coordinate
        if ( ok )
            coords[3] = myInitialExtent.midRef( posOld ).toDouble( &ok );

        if ( !ok )
        {
            QgsDebugMsg( QStringLiteral( "Error while parsing initial extent!" ) );
        }
        else
        {
            // set extent from parsed values
            QgsRectangle rect( coords[0], coords[1], coords[2], coords[3] );
            qRSIAGIS->setExtent( rect );
        }
    }

    if ( !pythonfile.isEmpty() )
    {
#ifdef Q_OS_WIN
        //replace backslashes with forward slashes
        pythonfile.replace( '\\', '/' );
#endif
        QgsPythonRunner::run( QStringLiteral( "with open('%1','r') as f: exec(f.read())" ).arg( pythonfile ) );
    }

    /////////////////////////////////`////////////////////////////////////
    // Take a snapshot of the map view then exit if snapshot mode requested
    /////////////////////////////////////////////////////////////////////
    if ( !mySnapshotFileName.isEmpty() )
    {
        /*You must have at least one paintEvent() delivered for the window to be
      rendered properly.

      It looks like you don't run the event loop in non-interactive mode, so the
      event is never occurring.

      To achieve this without running the event loop: show the window, then call
      qApp->processEvents(), grab the pixmap, save it, hide the window and exit.
      */
        //qRSIAGIS->show();
        myApp.processEvents();
        QPixmap *myQPixmap = new QPixmap( mySnapshotWidth, mySnapshotHeight );
        myQPixmap->fill();
        qRSIAGIS->saveMapAsImage( mySnapshotFileName, myQPixmap );
        myApp.processEvents();
        qRSIAGIS->hide();

        return 1;
    }

    if ( !dxfOutputFile.isEmpty() )
    {
        qRSIAGIS->hide();

        QgsDxfExport dxfExport;
        dxfExport.setSymbologyScale( dxfScale );
        dxfExport.setSymbologyExport( dxfSymbologyMode );
        dxfExport.setExtent( dxfExtent );

        QStringList layerIds;
        QList< QgsDxfExport::DxfLayer > layers;
        if ( !dxfMapTheme.isEmpty() )
        {
            const auto constMapThemeVisibleLayers = QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( dxfMapTheme );
            for ( QgsMapLayer *layer : constMapThemeVisibleLayers )
            {
                QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
                if ( !vl )
                    continue;
                layers << QgsDxfExport::DxfLayer( vl );
                layerIds << vl->id();
            }
        }
        else
        {
            const auto constMapLayers = QgsProject::instance()->mapLayers();
            for ( QgsMapLayer *ml : constMapLayers )
            {
                QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
                if ( !vl )
                    continue;
                layers << QgsDxfExport::DxfLayer( vl );
                layerIds << vl->id();
            }
        }

        if ( !layers.isEmpty() )
        {
            dxfExport.addLayers( layers );
        }

        QFile dxfFile;
        if ( dxfOutputFile == QLatin1String( "-" ) )
        {
            if ( !dxfFile.open( stdout, QIODevice::WriteOnly | QIODevice::Truncate ) )
            {
                std::cerr << "could not open stdout" << std::endl;
                return 2;
            }
        }
        else
        {
            if ( !dxfOutputFile.endsWith( QLatin1String( ".dxf" ), Qt::CaseInsensitive ) )
                dxfOutputFile += QLatin1String( ".dxf" );
            dxfFile.setFileName( dxfOutputFile );
        }

        QgsDxfExport::ExportResult res = dxfExport.writeToFile( &dxfFile, dxfEncoding );
        switch ( res )
        {
        case QgsDxfExport::ExportResult::Success:
            break;

        case QgsDxfExport::ExportResult::DeviceNotWritableError:
            std::cerr << "dxf output failed, the device is not wriable" << std::endl;
            break;

        case QgsDxfExport::ExportResult::InvalidDeviceError:
            std::cerr << "dxf output failed, the device is invalid" << std::endl;
            break;

        case QgsDxfExport::ExportResult::EmptyExtentError:
            std::cerr << "dxf output failed, the extent could not be determined" << std::endl;
            break;
        }

        delete qRSIAGIS;

        return static_cast<int>( res );
    }

    // make sure we don't have a dirty blank project after launch
    QgsProject::instance()->setDirty( false );

#ifdef HAVE_OPENCL

    // Overrides the OpenCL path to the folder containing the programs
    // - use the path specified with --openclprogramfolder,
    // - use the environment QGIS_OPENCL_PROGRAM_FOLDER if not found
    // - use a default location as a fallback (this is set in QgsApplication initialization)
    if ( openClProgramFolder.isEmpty() )
    {
        openClProgramFolder = getenv( "QGIS_OPENCL_PROGRAM_FOLDER" );
    }

    if ( ! openClProgramFolder.isEmpty() )
    {
        QgsOpenClUtils::setSourcePath( openClProgramFolder );
    }

#endif

    if ( takeScreenShots )
    {
        qRSIAGIS->takeAppScreenShots( screenShotsPath, screenShotsCategories );
    }

    /////////////////////////////////////////////////////////////////////
    // Continue on to interactive gui...
    /////////////////////////////////////////////////////////////////////
    qRSIAGIS->show();
    myApp.connect( &myApp, SIGNAL( lastWindowClosed() ), &myApp, SLOT( quit() ) );

    mypSplash->finish( qRSIAGIS );
    delete mypSplash;

    qRSIAGIS->completeInitialization();

#if defined(ANDROID)
    // fix for Qt Ministro hiding app's menubar in favor of native Android menus
    qRSIAGIS->menuBar()->setNativeMenuBar( false );
    qRSIAGIS->menuBar()->setVisible( true );
#endif

#if !defined(Q_OS_WIN)
    UnixSignalWatcher sigwatch;
    sigwatch.watchForSignal( SIGINT );

    QObject::connect( &sigwatch, &UnixSignalWatcher::unixSignal, &myApp, [&myApp ]( int signal )
    {
        switch ( signal )
        {
        case SIGINT:
            myApp.exit( 1 );
            break;

        default:
            break;
        }
    } );
#endif

    int retval = myApp.exec();
    delete qRSIAGIS;
    return retval;
}
